﻿''' <summary>
''' Helper for reading input from keyboard, gamepad, and touch input. This class 
''' tracks both the current and previous state of the input devices, and implements 
''' query methods for high level input actions such as "move up through the menu"
''' or "pause the game".
''' </summary>
Public Class InputState
#Region "Fields"

	Public Const MaxInputs% = 4

	Public ReadOnly CurrentKeyboardStates() As KeyboardState
	Public ReadOnly CurrentGamePadStates As GamePadState()

	Public ReadOnly LastKeyboardStates() As KeyboardState
	Public ReadOnly LastGamePadStates() As GamePadState

	Public ReadOnly GamePadWasConnected() As Boolean

	Public TouchState As TouchCollection

	Public ReadOnly Gestures As List(Of GestureSample) = New List(Of GestureSample)()

#End Region

#Region "Initialization"


	''' <summary>
	''' Constructs a new input state.
	''' </summary>
	Public Sub New()
		CurrentKeyboardStates = New KeyboardState(MaxInputs) {}
		CurrentGamePadStates = New GamePadState(MaxInputs) {}

		LastKeyboardStates = New KeyboardState(MaxInputs) {}
		LastGamePadStates = New GamePadState(MaxInputs) {}

		GamePadWasConnected = New Boolean(MaxInputs) {}
	End Sub


#End Region

#Region "Public Methods"


	''' <summary>
	''' Reads the latest state of the keyboard and gamepad.
	''' </summary>
	Public Sub Update()
		For i% = 1 To MaxInputs
			LastKeyboardStates(i) = CurrentKeyboardStates(i)
			LastGamePadStates(i) = CurrentGamePadStates(i)

			CurrentKeyboardStates(i) = Keyboard.GetState(CType(i, PlayerIndex))
			CurrentGamePadStates(i) = GamePad.GetState(CType(i, PlayerIndex))

			' Keep track of whether a gamepad has ever been
			' connected, so we can detect if it is unplugged.
			If (CurrentGamePadStates(i).IsConnected) Then _
				 GamePadWasConnected(i) = True

		Next

		TouchState = TouchPanel.GetState()

		Gestures.Clear()
		While (TouchPanel.IsGestureAvailable)
			Gestures.Add(TouchPanel.ReadGesture())
		End While
	End Sub


	''' <summary>
	''' Helper for checking if a key was newly pressed during this update. The
	''' controllingPlayer parameter specifies which player to read input for.
	''' If this is null, it will accept input from any player. When a keypress
	''' is detected, the output playerIndex reports which player pressed it.
	''' </summary>
	Public Function IsNewKeyPress(key As Keys, controllingPlayer As PlayerIndex?, _
										ByRef _playerIndex As PlayerIndex) As Boolean
		If (controllingPlayer.HasValue) Then
			' Read input from the specified player.
			_playerIndex = controllingPlayer.Value

			Dim i% = _PlayerIndex

			Return (CurrentKeyboardStates(i).IsKeyDown(key) And _
					LastKeyboardStates(i).IsKeyUp(key))
		Else
			' Accept input from any player.
			Return (IsNewKeyPress(key, PlayerIndex.One, _playerIndex) Or _
			  IsNewKeyPress(key, PlayerIndex.Two, _playerIndex) Or _
			  IsNewKeyPress(key, PlayerIndex.Three, _playerIndex) Or _
			  IsNewKeyPress(key, PlayerIndex.Four, _playerIndex))
		End If
	End Function

	''' <summary>
	''' Helper for checking if a button was newly pressed during this update.
	''' The controllingPlayer parameter specifies which player to read input for.
	''' If this is null, it will accept input from any player. When a button press
	''' is detected, the output playerIndex reports which player pressed it.
	''' </summary>
	Public Function IsNewButtonPress(button As Buttons, controllingPlayer As PlayerIndex?, _
												 ByRef _PlayerIndex As PlayerIndex) As Boolean
		If (controllingPlayer.HasValue) Then
			' Read input from the specified player.
			_PlayerIndex = controllingPlayer.Value

			Dim i% = _PlayerIndex

			Return (CurrentGamePadStates(i).IsButtonDown(button) And _
					LastGamePadStates(i).IsButtonUp(button))
		Else
			' Accept input from any player.
			Return (IsNewButtonPress(button, PlayerIndex.One, _PlayerIndex) Or _
					IsNewButtonPress(button, PlayerIndex.Two, _PlayerIndex) Or _
					IsNewButtonPress(button, PlayerIndex.Three, _PlayerIndex) Or _
				   IsNewButtonPress(button, PlayerIndex.Four, _PlayerIndex))
		End If
	End Function


	''' <summary>
	''' Checks for a "menu select" input action.
	''' The controllingPlayer parameter specifies which player to read input for.
	''' If this is null, it will accept input from any player. When the action
	''' is detected, the output playerIndex reports which player pressed it.
	''' </summary>
	Public Function IsMenuSelect(controllingPlayer As PlayerIndex?, _
							 ByRef Index As PlayerIndex) As Boolean
		Return IsNewKeyPress(Keys.Space, controllingPlayer, Index) Or _
	  IsNewKeyPress(Keys.Enter, controllingPlayer, Index) Or _
	  IsNewButtonPress(Buttons.A, controllingPlayer, Index) Or _
	  IsNewButtonPress(Buttons.Start, controllingPlayer, Index)
	End Function


	''' <summary>
	''' Checks for a "menu cancel" input action.
	''' The controllingPlayer parameter specifies which player to read input for.
	''' If this is null, it will accept input from any player. When the action
	''' is detected, the output playerIndex reports which player pressed it.
	''' </summary>
	Public Function IsMenuCancel(controllingPlayer As PlayerIndex?, _
							 ByRef Index As PlayerIndex) As Boolean
		Return IsNewKeyPress(Keys.Escape, controllingPlayer, Index) Or _
		 IsNewButtonPress(Buttons.B, controllingPlayer, Index) Or _
		 IsNewButtonPress(Buttons.Back, controllingPlayer, Index)
	End Function


	''' <summary>
	''' Checks for a "menu up" input action.
	''' The controllingPlayer parameter specifies which player to read
	''' input for. If this is null, it will accept input from any player.
	''' </summary>
	Public Function IsMenuUp(controllingPlayer As PlayerIndex?) As Boolean
		Dim Index As PlayerIndex

		Return IsNewKeyPress(Keys.Up, controllingPlayer, Index) Or _
			   IsNewButtonPress(Buttons.DPadUp, controllingPlayer, Index) Or _
			   IsNewButtonPress(Buttons.LeftThumbstickUp, controllingPlayer, Index)
	End Function


	''' <summary>
	''' Checks for a "menu down" input action.
	''' The controllingPlayer parameter specifies which player to read
	''' input for. If this is null, it will accept input from any player.
	''' </summary>
	Public Function IsMenuDown(controllingPlayer As PlayerIndex?) As Boolean
		Dim Index As PlayerIndex

		Return IsNewKeyPress(Keys.Down, controllingPlayer, Index) Or _
				   IsNewButtonPress(Buttons.DPadDown, controllingPlayer, Index) Or _
  IsNewButtonPress(Buttons.LeftThumbstickDown, controllingPlayer, Index)
	End Function

	''' <summary>
	''' Checks for a "pause the game" input action.
	''' The controllingPlayer parameter specifies which player to read
	''' input for. If this is null, it will accept input from any player.
	''' </summary>
	Public Function IsPauseGame(controllingPlayer As PlayerIndex?) As Boolean
		Dim _PlayerIndex As PlayerIndex

		Return IsNewKeyPress(Keys.Escape, controllingPlayer, _PlayerIndex) Or _
		 IsNewButtonPress(Buttons.Back, controllingPlayer, _PlayerIndex) Or _
		 IsNewButtonPress(Buttons.Start, controllingPlayer, _PlayerIndex)
	End Function


#End Region
End Class